package com.zlsy.thread.threadsafe;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author zhouliang
 * @date 2020/03/18
 **/
@Slf4j
public class MutilateThreadError implements Runnable {

    int index = 0;
    private CountDownLatch latch;
    private int n;

    final boolean[] marked = new boolean[100000];
    static AtomicInteger realIndex = new AtomicInteger();
    static AtomicInteger worryCount = new AtomicInteger();
    volatile CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);
    volatile CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);


    public MutilateThreadError(CountDownLatch latch, int n) {
        this.latch = latch;
        this.n = n;
    }


    public static void main(String[] args) throws InterruptedException {
        int n = 10000;
        CountDownLatch latch = new CountDownLatch(n * 2);
        MutilateThreadError instance = new MutilateThreadError(latch, n);

        Thread thread = new Thread(instance);
        Thread thread1 = new Thread(instance);
        thread.start();
        thread1.start();
        latch.await();

        log.info("表面上得出的结果：{}", instance.index);
        log.info("真正运行的次数:{}", realIndex.get());
        log.info("错误次数:{}", worryCount.get());

    }

    @Override
    public void run() {
        marked[0] = true;
        for (int i = 0; i < n; i++) {
            try {
                //同时进行index++
                try {
                    cyclicBarrier2.reset();
                    cyclicBarrier1.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
                index++;
                //同时保证index++结束，这两个同时就保证不会出现下面的一、二、三问题点了
                try {
                    cyclicBarrier1.reset();
                    cyclicBarrier2.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
                //真正运行的次数
                realIndex.incrementAndGet();
                //下标index默认为false，如果已经为true了代表别的线程已经执行过了
                synchronized (this) {
                    //一、如果没加synchronized，假设第二个线程进来也是读到了这个index，然后判断是否有前面也读到这个index的线程已经执行过了
                    //由于可能存在，线程1执行了index++，但是还没进行marked的时候，线程2已经进行判断了，
                    //那么此时其实应该为true，却是false

                    //二、如果加了synchronized同步块，线程2抢到了锁，线程1和线程2没法同时进入这段代码了，但是如果
                    // synchronized (this)到marked[index]这段代码之间，cpu把资源调度到线程1了，线程2仍然持有锁
                    //这时候index 从线程1和线程2进来的1，又被线程1加到了2，然后又切换回了线程2，线程2判断的时候index
                    //为false, 这个时候是不是觉得worryCount应该比实际的小了，错了！实际运行错误次数+表面的结果是大于2W
                    if (marked[index] && marked[index - 1]) {
                        System.out.println("发生错误" + index);
                        //发生错误的次数
                        worryCount.incrementAndGet();
                    }
                    marked[index] = true;
                }
            } finally {
                latch.countDown();
            }
        }
    }
}
